/* 
 * File:   ResourceManager.h
 * Author: EnviousCreep & RedEyedKiller :D
 *
 * Created on March 23, 2010, 3:31 PM
 */

#ifndef _RESOURCEPOOL_H
#define	_RESOURCEPOOL_H

#include <map>
#include <string>
#include "Resource.h"
#include "Utilities.h"
#include "Logger.h"
#include "parsers/ResourceLoaders.h"

/**
 * A template class which handles Resources of Type.
 * Should not be createad and should be used only via ResourceManager.
 */
template<typename Type>
class ResourcePool
{
    //the type of the Resource
    typedef Resource<Type> RType;
    //the type of the map. Note that before this type
    //keyword typename must exist.
    typedef std::map<std::string, RType*> DataMap;

public:

    ResourcePool()
    {
        refCounter = 0;
        decreaseLimit = 50;
    }

    ~ResourcePool()
    {
        FreeAll();
    }

    /**
     * The main method of ResourcePool. Returns the specified resource.
     * If the specified resource is not loaded yet this method loads it
     * with the help of Loader object.
     * 
     * If Loader fails an error is logged and this will try yo load a default resource.
     * If it fails again NULL wil be returned.
     * 
     * @param requested - The key of the resource
     * @return pointer to the resource or NULL.
     */
    RType* RequestResource(const Loader<Type>& loader)
    {
        if (!SearchResource(loader.GetKey())) //exists??
        {// doesnt exist in the map so create the resource    

            // the ptr to store data of loader.
            Type* tmpData = NULL;
            if (loader(&tmpData, path))
            {
                //wrap data with a Resource
                RType* data = new Resource<Type > (tmpData);
                //store it
                dataMap[loader.GetKey()] = data;
                //and increase its reference counter.
                data->IncreaseReference();
                return data;
            }
            else
            {
                //report error.
                LOG(Logger::CHANNEL_LOADING, LogFileStream::LEVEL_ERROR) << " File " <<
                        loader.GetKey() << " @ (" << path << ") could not be loaded succesfully";
                return NULL;
            }
        }
        else // exists
        {
            dataMap[loader.GetKey()]->IncreaseReference(); //INCREASE REFERENCE
            return dataMap[loader.GetKey()];
        }
    }

    /**
     * Deletes all resources and then clears the map.
     */
    void FreeAll()
    {
        SafeRelease<std::string, RType* >(dataMap);
    }

    void MakeImportant(const std::string& toSet)
    {
        if (SearchResource(toSet.c_str()))
            dataMap[toSet]->SetImportant();
    }

    void MakeImportant(const char* toSet)
    {
        if (SearchResource(toSet))
            dataMap[toSet]->SetImportant();
    }

    void MakeUnimportant(const std::string& toSet)
    {
        if (SearchResource(toSet.c_str()))
            dataMap[toSet]->SetUnimportant();
    }

    void MakeUnimportant(const char* toSet)
    {
        if (SearchResource(toSet))
            dataMap[toSet]->SetUnimportant();
    }

    void GarbageCollection(bool killEmAll = false)
    {
        std::vector<std::string> vectorToErase;
        int i = 0;
        typename DataMap::iterator iter = dataMap.begin();
        typename DataMap::const_iterator end = dataMap.end();
        for (; iter != end; ++iter)
        {
            if ((!iter->second->IsImportant() || killEmAll) && iter->second->GetReferences() == 0)
            {
                vectorToErase.push_back(iter->first);
                delete(iter->second);
            }
            i++;
        }

        for (std::vector<std::string>::iterator iter = vectorToErase.begin(); iter != vectorToErase.end(); ++iter)
            dataMap.erase(*iter);
    }
    
    void IncreaseRefCounter()
    {
        refCounter++;
        if (refCounter == decreaseLimit)
        {
            GarbageCollection();
            refCounter = 0;
        }
    }

    void IncreaseReference(const char* toIncrease)
    {
        dataMap[toIncrease]->IncreaseReference();
    }

    //void ResourceManager::increaseReference(std::string toIncrease)
    //{
    //    surfaceMap[toIncrease]->increaseReference();
    //}

    void DecreaseReference(const char* toDecrease)
    {
        dataMap[toDecrease]->DecreaseReference();
        IncreaseRefCounter();
    }

    //void ResourceManager::decreaseReference(std::string toDecrease)
    //{
    //    surfaceMap[toDecrease]->decreaseReference();
    //    increaseRefCounter();
    //}

    void SetDecreaseLimit(int decreaseLimit)
    {
        this->decreaseLimit = decreaseLimit;
    }

    /**
     * Sets the default path to the Type flder.
     * @param path
     */
    void SetPath(const std::string& path)
    {
        this->path = path;
    }

    void SetDefaultResource(const std::string& defaultResource)
    {
        this->defaultResource = defaultResource;
    }

protected:

    /**
     * Return true if toFind exists within the map.
     * @param toFind 
     * @return 
     */
    bool SearchResource(const char* toFind)
    {
        return (dataMap.find(toFind) != dataMap.end());
    }

    /**
     * Returns true if file exists.
     * @param requested
     * @return 
     */
    bool FileExists(const char* requested)
    {
        FILE* fp = fopen(requested, "r");
        if (fp != NULL)
        {
            fclose(fp);
            return true;
        }
        else
        {
            return false;
        }
    }

private:
    /**
     * The hash map tha contains all resources.
     */
    DataMap dataMap;

    /**
     * A number to track how many free ups have happened.
     */
    int refCounter;

    /**
     * When refCounter reaches decreaseLimit then garbage collector is called.
     */
    int decreaseLimit;

    /**
     * The deafault path to resources.
     */
    std::string path;

    /**
     * The name of the default resource.
     */
    std::string defaultResource;
};

#endif	/* _RESOURCEPOOL_H */

